#pragma once

#include<iostream>
#include<queue>
#include<pthread.h>

const int gmaxcap = 5;

template<class T>
class BolockQueue
{
public:
public:
    BolockQueue(int maxcap = gmaxcap):_maxcap(maxcap)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_pcond,nullptr);
        pthread_cond_init(&_ccond,nullptr);
    }
    void push(const T& in)// 输入型参数 const &
    {
        pthread_mutex_lock(&_mutex);
        // 1.判断
        // 细节2：充当条件判断的语法必须是while，不能用if（因为我可能一批线程被唤醒，有些线程可能是不满足条件的，必须再次判断
        while(is_full())//bug??
        {
            // 细节1：pthread_cond_wait第二个参数必须是一把锁
            // a.pthread_cond_wait：该函数调用的时候，会以原子性的方式，将锁释放
            // b.pthread_cond_wait：该函数在被唤醒返回的时候，会自动的重新唤醒你传入的锁
            pthread_cond_wait(&_pcond,&_mutex);// 因为生产条件不满足，无法生产，此时我们的生产者进行等待
        }
        // 2.走到这里一定没满？
        _q.push(in);
        // 3.绝对能保证，阻塞队列里一定有数据
        // 细节3：pthread_cond_signal：这个函数可以放在临界区内部，也可以放在外部
        pthread_cond_signal(&_ccond);// 这里可以有一定的策略
        pthread_mutex_unlock(&_mutex);
        // pthread_cond_signal(&_ccond);
    }
    void pop(T* out)// 输出型 * 输入输出行 &
    {
        pthread_mutex_lock(&_mutex);
        // 1.判断
        while(is_empty())
        {
            pthread_cond_wait(&_ccond,&_mutex);// 消费者休眠
        }
        // 2.一定不为空
        *out = _q.front();
        _q.pop();
        // 3.至少有一个空的位置
        pthread_cond_signal(&_pcond);
        pthread_mutex_unlock(&_mutex);
    }
    ~BolockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_pcond);
        pthread_cond_destroy(&_ccond);
    }
private:
    bool is_full()
    {
        return _q.size() == _maxcap;
    }
    bool is_empty()
    {
        return _q.empty();
    }
private:
    std::queue<T> _q;
    int _maxcap;// 队列元素上限
    pthread_mutex_t _mutex;// 不在类外初始化，所以不需要用指针
    pthread_cond_t _pcond;// 生产者对应的条件变量
    pthread_cond_t _ccond;// 生消费者者对应的条件变量
};